const express = require('express');
const router = express.Router();
const gm = require('gm').subClass({imageMagick: true});
const fs = require('fs');
const path = require('path');
const Utils = require("../util/Utils");
const FileUtil = require("../util/FileUtil");
const CrawlerUtil = require("../util/CrawlerUtil");
const CanvasUtil = require("../util/CanvasUtil");
const config = require('../config/config')

router.get('/:owner/:repo', async function (req, res, next) {
    await renderImg(req, res, 10)
});
router.get('/:owner/:repo/:num', async function (req, res, next) {
    let num = 10;
    try {
        num = req.params.num
    } catch (e) {
    }
    await renderImg(req, res, num)
});

async function renderImg(req, res, splitNum) {
    // 仓库所有者
    const owner = req.params.owner
    // 仓库名
    const repo = req.params.repo
    // 存放贡献者头像的目录
    const userAvatarTempDir = config.contributors.contributorsDir + `/${owner}/${repo}/avatar`;
    // 按照阈值将贡献者头像横向排列保存的目录
    const outTempDir = config.contributors.contributorsDir + `/${owner}/${repo}/outTemp`;
    // 将 outTempDir 目录中的文件拼装成完成图片后保存的目录
    const outDir = config.contributors.contributorsDir + `/${owner}/${repo}/out`;

    // 优先判断文件夹是否存在，不存在则创建
    await FileUtil.dirExists(userAvatarTempDir)
    await FileUtil.dirExists(outDir)
    await FileUtil.dirExists(outTempDir)

    // 最终写出到浏览器端的图片文件名，按照每行放置的图片个数区分图片，方便重复利用
    const resFileName = `${owner}${repo}_${splitNum}.png`
    // 最终写出到浏览器端的图片文件路径
    const outFilePath = path.join(outDir, resFileName);

    // 当图片已经存在时，直接返回
    if (await FileUtil.isFileExisted(outFilePath)) {
        Utils.writeImgToResponse(outFilePath, res, false);
        return;
    }

    // 抓取图片
    const url = `https://gitee.com/${owner}/${repo}/contributors`
    CrawlerUtil.grab(url, async function (avatars) {
        await downloadAvatar(avatars, userAvatarTempDir);

        // 最多等 10 分钟
        let timeout = 10 * 60 * 1000;
        let delay = 2000;
        let timer = setInterval(function () {
            let fileList = fs.readdirSync(userAvatarTempDir);
            if (timeout <= 0) {
                console.log(`延时过高，已下载完成 ${fileList.length} 个图片，剩余 ${avatars.length - fileList.length} 个`)
                clearInterval(timer)
                // 先退出不处理。
                return;
            }
            console.log(`已下载完成 ${fileList.length} 个图片，剩余 ${avatars.length - fileList.length} 个`)
            if (fileList && fileList.length >= avatars.length) {
                clearInterval(timer)
                createAndResponseImg(userAvatarTempDir, outDir, outTempDir, outFilePath, splitNum, res)
            } else {
                timeout -= delay
            }
        }, delay)
    }, 1, [])
}

/**
 * 将项目贡献者的头像组装成完整图片后输出
 * @param userAvatarTempDir
 * @param outDir
 * @param outTempDir
 * @param outFilePath
 * @param splitNum
 * @param res
 * @returns {Promise<void>}
 */
async function createAndResponseImg(userAvatarTempDir, outDir, outTempDir, outFilePath, splitNum, res) {
    const border = 0.5

    await horizontalCompositePicture(userAvatarTempDir, splitNum, outTempDir, border);

    await verticalCompositePicture(outTempDir, border, outFilePath);

    Utils.writeImgToResponse(outFilePath, res, false);
}

/**
 * 将贡献者的头像按照指定个数横向合成多张图片
 * @param userAvatarTempDir
 * @param splitNum
 * @param outTempDir
 * @param border
 * @returns {Promise<null>}
 */
async function horizontalCompositePicture(userAvatarTempDir, splitNum, outTempDir, border) {
    let gmInstance = null
    // 先横向组装单张图片
    let fileList = fs.readdirSync(userAvatarTempDir);
    // 正序排序
    fileList.sort(function f(a, b) {
        const i = parseInt(a.split(".")[0])
        const j = parseInt(b.split(".")[0])
        return i > j ? 1 : -1;
    })
    let targetTempFilePath = ''
    for (let i in fileList) {
        const index = parseInt(i)
        const offset = index % splitNum
        if (offset === 0) {
            if (null !== gmInstance) {
                await writeImg(gmInstance, targetTempFilePath)
            }
            gmInstance = await gm(path.join(userAvatarTempDir, fileList[index]));
            targetTempFilePath = path.join(outTempDir, index + ".png")
        } else {
            let rightFile = path.join(userAvatarTempDir, fileList[index]);
            // true 从左到右拼接
            // .border(width, height)
            gmInstance = await gmInstance.border(border, border).append(rightFile, true)
        }
        if (index === fileList.length - 1) {
            // 最后一个图片了，执行合并输出的操作
            await writeImg(gmInstance, targetTempFilePath)
        }
    }
}

/**
 * 将横向合成后的图片再按照顺序纵向合成一张整图（输出到浏览器端的最终图片）
 * @param outTempDir
 * @param border
 * @param outFilePath
 * @returns {Promise<void>}
 */
async function verticalCompositePicture(outTempDir, border, outFilePath) {
    // 再将横向拼接好的图片纵向拼接
    let gmInstance = null;
    let tempFileList = fs.readdirSync(outTempDir);
    gmInstance = await gm(path.join(outTempDir, tempFileList[0]));
    for (let i in tempFileList) {
        const index = parseInt(i)
        if (index > 0) {
            let rightFile = path.join(outTempDir, tempFileList[index]);
            gmInstance = await gmInstance.append(rightFile)
        }
        if (index === tempFileList.length - 1) {
            // 最后一个图片了，执行合并输出的操作
            await writeImg(gmInstance, outFilePath)
        }
    }
    for (let tempIndex in tempFileList) {
        const temp = path.join(outTempDir, tempFileList[parseInt(tempIndex)]);
        // 删除临时文件
        FileUtil.deleteFile(temp)
    }
}

/**
 * 将网络图片下载到本地，针对无法获取到图片的贡献者，使用其名字的首字符创建图片并保存到本地
 * @param avatars
 * @param userAvatarTempDir
 * @returns {Promise<boolean>}
 */
async function downloadAvatar(avatars, userAvatarTempDir) {
    for (let i in avatars) {
        const index = parseInt(i)
        const avatar = avatars[index]
        if (!avatar) {
            continue
        }
        const filePath = userAvatarTempDir + "/" + i + ".png"
        if (await FileUtil.isFileExisted(filePath)) {
            continue;
        }
        if (avatar.substr(0, 4) !== 'http') {
            await CanvasUtil.draw(filePath, avatar)
        } else {
            await Utils.downloadImg(avatar, filePath)
        }
    }
    return true;
}

/**
 * 使用 gm 插件生成合并后的图片
 * @param gm
 * @param target
 * @returns {Promise<unknown>}
 */
function writeImg(gm, target) {
    return new Promise((r, j) => {
        gm.write(target, err => {
            if (err) {
                console.log(err)
            }
            r();
        })
    })
}

module.exports = router;
